package libRender;

import engine.render.type.Tri;
import engine.res.Bitmap;
import math.Maths;
import math.vector.Vecmath;
import math.vector.vec3f;

public class Quadric 
{
    
    private static Quadric singleton;
    
    public static Quadric gen() 
    {
        if (singleton == null) 
        {
            singleton = new Quadric();
        }
        return singleton;
    }
    
    private Quadric() 
    {
    }
    
    public static Tri[] getCylinderXZ(float ylow, float yhigh, int slices, float radius, Bitmap tex) 
    {
        int nTrisFan = slices;
        int nTrisLateral = 2 * slices;
        Tri[] tris = new Tri[2 * nTrisFan];
        vec3f v0 = new vec3f(0, ylow, 0);
        vec3f v1 = new vec3f(0, yhigh, 0);
        vec3f axis = new vec3f(Vecmath.AXIS_Y);
        vec3f perp = new vec3f(Vecmath.AXIS_X);
        float rec_twopi = 1.0f / Maths.PI_TIMES_2;
        float angle = Maths.PI_TIMES_2 / nTrisFan;
        vec3f[] dirs = new vec3f[nTrisFan];
        float[] tcs = new float[nTrisFan];
        for (int i = 0; i < nTrisFan; i++) 
        {
            dirs[i] = Vecmath.rotate(perp, axis, i * angle);
            dirs[i].scalarMult(radius);
            tcs[i] = (i * angle) * rec_twopi;
        }
        int indTris = 0;
        for (int i = 0; i < nTrisFan; i++) 
        {
            vec3f vert0 = Vecmath.add(v0, dirs[i]);
            vec3f vert1 = Vecmath.add(v0, dirs[Maths.modi(i + 1, nTrisFan)]);
            Tri tri = new Tri(
                v0, 0, 0, 
                vert0, tcs[i], 1, 
                vert1, tcs[Maths.modi(i + 1, nTrisFan - 1)], 1
            );
            tri.normal(0, -1, 0);
            tri.texture(tex);
            tris[indTris++] = tri;
        }
        for (int i = 0; i < nTrisFan; i++) 
        {
            vec3f vert0 = Vecmath.add(v1, dirs[i]);
            vec3f vert1 = Vecmath.add(v1, dirs[Maths.modi(i + 1, nTrisFan)]);
            Tri tri = new Tri(
                v1, 0, 0, 
                vert0, tcs[i], 1, 
                vert1, tcs[Maths.modi(i + 1, nTrisFan - 1)], 1
            );
            tri.normal(0, 1, 0);
            tri.texture(tex);
            tris[indTris++] = tri;
        }
        return tris;
    }
    
    public static Tri[] getCylinder(vec3f v0, vec3f v1, int slices, float radius, Bitmap tex) 
    {
        int nTrisFan = slices;
        int nTrisLateral = 2 * slices;
        Tri[] tris = new Tri[2 * nTrisFan];
        int indTris = 0;
        vec3f axis = Vecmath.subtract(v1, v0);
        vec3f fanNormv0 = axis.getUnitVectorCopy();
        vec3f fanNormv1 = fanNormv0.getInverted();
//        vec3f perp = axis.getPerpendicVec();
        vec3f perp = Vecmath.crossProduct(fanNormv0, Vecmath.AXIS_X);
        perp.normalize();
//        perp.scalarMult(radius);
        float rec_twopi = 1.0f / Maths.PI_TIMES_2;
        float angle = Maths.PI_TIMES_2 / nTrisFan;
        vec3f[] dirs = new vec3f[nTrisFan];
        float[] tcs = new float[nTrisFan];
        for (int i = 0; i < nTrisFan; i++) 
        {
            dirs[i] = Vecmath.rotate(perp, axis, i * angle);
            dirs[i].scalarMult(radius);
            tcs[i] = (i * angle) * rec_twopi;
        }
        for (int i = 0; i < nTrisFan - 1; i++) 
        {
            vec3f vert0 = Vecmath.add(v0, dirs[i]);
            vec3f vert1 = Vecmath.add(v0, dirs[Maths.modi(i + 1, nTrisFan - 1)]);
            Tri tri = new Tri(
                v0, 0, 0, 
                vert0, tcs[i], 1, 
                vert1, tcs[Maths.modi(i + 1, nTrisFan - 1)], 1
            );
            tri.normal(fanNormv0);
            tri.texture(tex);
            tris[indTris++] = tri;
        }
        for (int i = 0; i < nTrisFan - 1; i++) 
        {
            vec3f vert0 = Vecmath.add(v1, dirs[i]);
            vec3f vert1 = Vecmath.add(v1, dirs[Maths.modi(i + 1, nTrisFan - 1)]);
            Tri tri = new Tri(
                v1, 0, 0, 
                vert0, tcs[i], 1, 
                vert1, tcs[Maths.modi(i + 1, nTrisFan - 1)], 1
            );
            tri.normal(fanNormv1);
            tri.texture(tex);
            tris[indTris++] = tri;
        }
        Tri[] returntris = new Tri[indTris];
        for (int i = 0; i < indTris; i++) 
        {
            returntris[i] = tris[i];
        }
        return returntris;
    }
    
}
